﻿//Candle.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Candles
{
	using System;
	using System.Runtime.Serialization;

	using Ecng.Common;

	using StockSharp.BusinessEntities;

	/// <summary>
	/// Состояния свечки.
	/// </summary>
	public enum CandleStates
	{
		/// <summary>
		/// Пустое состояние (свечка отсутствует).
		/// </summary>
		None,

		/// <summary>
		/// Свечка запущена на формирование.
		/// </summary>
		Started,

		/// <summary>
		/// Свечка изменена.
		/// </summary>
		Changed,

		/// <summary>
		/// Свечка закончена.
		/// </summary>
		Finished,
	}

	/// <summary>
	/// Базовый класс для свечки (содержит основные параметры).
	/// </summary>
	[DataContract]
	[Serializable]
	[KnownType(typeof(TickCandle))]
	[KnownType(typeof(VolumeCandle))]
	[KnownType(typeof(RangeCandle))]
	[KnownType(typeof(TimeFrameCandle))]
	[KnownType(typeof(PnFCandle))]
	[KnownType(typeof(RenkoCandle))]
	public abstract class Candle
	{
		/// <summary>
		/// Инструмент.
		/// </summary>
		[DataMember]
		public Security Security { get; set; }

		private DateTime _openTime;

		/// <summary>
		/// Время начала свечки.
		/// </summary>
		[DataMember]
		public DateTime OpenTime
		{
			get { return _openTime; }
			set
			{
				ThrowIfFinished();
				_openTime = value;
			}
		}

		private DateTime _closeTime;

		/// <summary>
		/// Время окончания свечки.
		/// </summary>
		[DataMember]
		public DateTime CloseTime
		{
			get { return _closeTime; }
			set
			{
				ThrowIfFinished();
				_closeTime = value;
			}
		}

		private DateTime _highTime;

		/// <summary>
		/// Время с максимальной ценой в свечке.
		/// </summary>
		[DataMember]
		public DateTime HighTime
		{
			get { return _highTime; }
			set
			{
				ThrowIfFinished();
				_highTime = value;
			}
		}

		private DateTime _lowTime;

		/// <summary>
		/// Время с минимальной ценой в свечке.
		/// </summary>
		[DataMember]
		public DateTime LowTime
		{
			get { return _lowTime; }
			set
			{
				ThrowIfFinished();
				_lowTime = value;
			}
		}

		private decimal _openPrice;

		/// <summary>
		/// Цена открытия.
		/// </summary>
		[DataMember]
		public decimal OpenPrice
		{
			get { return _openPrice; }
			set
			{
				ThrowIfFinished();
				_openPrice = value;
			}
		}

		private decimal _closePrice;

		/// <summary>
		/// Цена закрытия.
		/// </summary>
		[DataMember]
		public decimal ClosePrice
		{
			get { return _closePrice; }
			set
			{
				ThrowIfFinished();
				_closePrice = value;
			}
		}

		private decimal _highPrice;

		/// <summary>
		/// Максимальная цена.
		/// </summary>
		[DataMember]
		public decimal HighPrice
		{
			get { return _highPrice; }
			set
			{
				ThrowIfFinished();
				_highPrice = value;
			}
		}

		private decimal _lowPrice;

		/// <summary>
		/// Минимальная цена.
		/// </summary>
		[DataMember]
		public decimal LowPrice
		{
			get { return _lowPrice; }
			set
			{
				ThrowIfFinished();
				_lowPrice = value;
			}
		}

		private decimal _totalPrice;

		/// <summary>
		/// Суммарный оборот по сделкам.
		/// </summary>
		[DataMember]
		public decimal TotalPrice
		{
			get { return _totalPrice; }
			set
			{
				ThrowIfFinished();
				_totalPrice = value;
			}
		}

		private decimal _openVolume;

		/// <summary>
		/// Объем открытия.
		/// </summary>
		[DataMember]
		public decimal OpenVolume
		{
			get { return _openVolume; }
			set
			{
				ThrowIfFinished();
				_openVolume = value;
			}
		}

		private decimal _closeVolume;

		/// <summary>
		/// Объем закрытия.
		/// </summary>
		[DataMember]
		public decimal CloseVolume
		{
			get { return _closeVolume; }
			set
			{
				ThrowIfFinished();
				_closeVolume = value;
			}
		}

		private decimal _highVolume;

		/// <summary>
		/// Максимальный объем.
		/// </summary>
		[DataMember]
		public decimal HighVolume
		{
			get { return _highVolume; }
			set
			{
				ThrowIfFinished();
				_highVolume = value;
			}
		}

		private decimal _lowVolume;

		/// <summary>
		/// Минимальный объем.
		/// </summary>
		[DataMember]
		public decimal LowVolume
		{
			get { return _lowVolume; }
			set
			{
				ThrowIfFinished();
				_lowVolume = value;
			}
		}

		private decimal _totalVolume;

		/// <summary>
		/// Суммарный объем.
		/// </summary>
		[DataMember]
		public decimal TotalVolume
		{
			get { return _totalVolume; }
			set
			{
				ThrowIfFinished();
				_totalVolume = value;
			}
		}

		private decimal _relativeVolume;

		/// <summary>
		/// Относительный объем.
		/// </summary>
		[DataMember]
		public decimal RelativeVolume
		{
			get { return _relativeVolume; }
			set
			{
				ThrowIfFinished();
				_relativeVolume = value;
			}
		}

		[field: NonSerialized]
		private CandleSeries _series;

		/// <summary>
		/// Серия свечек.
		/// </summary>
		public CandleSeries Series
		{
			get { return _series; }
			set { _series = value; }
		}

		[field: NonSerialized]
		private ICandleManagerSource _source;

		/// <summary>
		/// Источник, из которого была получена свечка.
		/// </summary>
		public ICandleManagerSource Source
		{
			get { return _source; }
			set { _source = value; }
		}

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public abstract object Arg { get; set; }

		/// <summary>
		/// Состояние.
		/// </summary>
		[DataMember]
		public CandleStates State { get; set; }

		/// <summary>
		/// Получить строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return "{0:HH:mm:ss} {1} (O:{2}, H:{3}, L:{4}, C:{5}, V:{6})"
				.Put(OpenTime, Series == null ? GetType().Name + "_" + Security + "_" + Arg : Series.ToString(),
						OpenPrice, HighPrice, LowPrice, ClosePrice, TotalVolume);
		}

		private void ThrowIfFinished()
		{
			if (State == CandleStates.Finished)
				throw new InvalidOperationException("Невозможно изменить сформированную свечку.");
		}
	}

	/// <summary>
	/// Свечка, группируемая по тайм-фрейму.
	/// </summary>
	[DataContract]
	[Serializable]
	public class TimeFrameCandle : Candle
	{
		/// <summary>
		/// Тайм-фрейм.
		/// </summary>
		[DataMember]
		public TimeSpan TimeFrame { get; set; }

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public override object Arg
		{
			get { return TimeFrame; }
			set { TimeFrame = (TimeSpan)value; }
		}
	}

	/// <summary>
	/// Свечка, группируемая по количеству сделок.
	/// </summary>
	[DataContract]
	[Serializable]
	public class TickCandle : Candle
	{
		/// <summary>
		/// Максимальное количество сделок, которое может содержать свечка.
		/// </summary>
		[DataMember]
		public int MaxTradeCount { get; set; }

		/// <summary>
		/// Текущее количество сделок, которое содержит свечка.
		/// </summary>
		[DataMember]
		public int CurrentTradeCount { get; set; }

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public override object Arg
		{
			get { return MaxTradeCount; }
			set { MaxTradeCount = (int)value; }
		}
	}

	/// <summary>
	/// Свечка, группируемая по количеству контрактов.
	/// </summary>
	[DataContract]
	[Serializable]
	public class VolumeCandle : Candle
	{
		/// <summary>
		/// Максимальное количество контрактов, которое может содержать свечка.
		/// </summary>
		[DataMember]
		public decimal Volume { get; set; }

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public override object Arg
		{
			get { return Volume; }
			set { Volume = (decimal)value; }
		}
	}

	/// <summary>
	/// Свечка, группируемая по ценовому диапазону.
	/// </summary>
	[DataContract]
	[Serializable]
	public class RangeCandle : Candle
	{
		/// <summary>
		/// Дельта цены, в рамках которой свечка может содержать сделки.
		/// </summary>
		[DataMember]
		public Unit PriceRange { get; set; }

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public override object Arg
		{
			get { return PriceRange; }
			set { PriceRange = (Unit)value; }
		}
	}

	/// <summary>
	/// Типы символов.
	/// </summary>
	[DataContract]
	[Serializable]
	public enum PnFTypes
	{
		/// <summary>
		/// Крестики (цена растет).
		/// </summary>
		[EnumMember]
		X,

		/// <summary>
		/// Нолики (цена падает).
		/// </summary>
		[EnumMember]
		O,
	}

	/// <summary>
	/// Значение параметров пункто-цифрового графика (график крестики-нолики).
	/// </summary>
	[DataContract]
	[Serializable]
	public class PnFArg : Equatable<PnFArg>
	{
		/// <summary>
		/// Изменение цены, при превышении которого регистрируется новый <see cref="PnFTypes.X"/> или <see cref="PnFTypes.O"/>.
		/// </summary>
		[DataMember]
		public Unit BoxSize { get; set; }

		/// <summary>
		/// Величина противоположного движения цены, при котором происходит смена <see cref="PnFTypes.X"/> на <see cref="PnFTypes.O"/> (или наоборот).
		/// </summary>
		[DataMember]
		public int ReversalAmount { get; set; }

		/// <summary>
		/// Получить строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return "Box = {0} RA = {1}".Put(BoxSize, ReversalAmount);
		}

		/// <summary>
		/// Создать копию объекта <see cref="PnFArg"/>.
		/// </summary>
		/// <returns>Копия.</returns>
		public override PnFArg Clone()
		{
			return new PnFArg
			{
				BoxSize = BoxSize.Clone(),
				ReversalAmount = ReversalAmount,
			};
		}

		/// <summary>
		/// Сравнить на эквивалентность.
		/// </summary>
		/// <param name="other">Значение параметров пункто-цифрового графика, с которым необходимо сделать сравнение.</param>
		/// <returns>True, если значения равны. Иначе, false.</returns>
		protected override bool OnEquals(PnFArg other)
		{
			return other.BoxSize == BoxSize && other.ReversalAmount == ReversalAmount;
		}

		/// <summary>
		/// Рассчитать хеш-код объекта <see cref="PnFArg"/>.
		/// </summary>
		/// <returns>Хеш-код.</returns>
		public override int GetHashCode()
		{
			return BoxSize.GetHashCode() ^ ReversalAmount.GetHashCode();
		}
	}

	/// <summary>
	/// Свечка пункто-цифрового графика (график крестики-нолики).
	/// </summary>
	[DataContract]
	[Serializable]
	public class PnFCandle : Candle
	{
		/// <summary>
		/// Значение параметров.
		/// </summary>
		[DataMember]
		public PnFArg PnFArg { get; set; }

		/// <summary>
		/// Тип символов.
		/// </summary>
		[DataMember]
		public PnFTypes Type { get; set; }

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public override object Arg
		{
			get { return PnFArg; }
			set { PnFArg = (PnFArg)value; }
		}
	}

	/// <summary>
	/// Свечка Рэнко графика.
	/// </summary>
	[DataContract]
	[Serializable]
	public class RenkoCandle : Candle
	{
		/// <summary>
		/// Изменение цены, при превышении которого регистрируется новая свечка.
		/// </summary>
		[DataMember]
		public Unit BoxSize { get; set; }

		/// <summary>
		/// Параметр свечки.
		/// </summary>
		public override object Arg
		{
			get { return BoxSize; }
			set { BoxSize = (Unit)value; }
		}
	}
}
